package com.wuuxiang.i5xforyou.utils;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.util.FileCopyUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.wuuxiang.i5xforyou.common.consts.Consts;

/**
 * ClassName: HttpUtil <br/>
 * Function: TODO ADD FUNCTION. <br/>
 * Reason: TODO ADD REASON(可选). <br/>
 * date: 2014年11月28日 上午10:11:02 <br/>
 *
 * @author 李鑫
 * @version
 * @since JDK 1.7
 */

public final class HttpUtil {

	private static String isrsa = "0"; //o2oapi项目中使用的加密头

//	private static PoolingHttpClientConnectionManager cm = null;//httpClinet的连接池

	private static final Log log = LogFactory.getLog(HttpUtil.class);

	public static final Map<Integer,String> HTTP_RETURN_CODE = new HashMap<Integer,String>();

	//httpClient的最大请求并发数,cpu数*2+2,太大的话，创建和回收线程都需要时间，反而性能不好
	private static final int HTTP_CLIENT_MAX_THREAD_COUNTS = 10;

	static{
		HTTP_RETURN_CODE.put(100, "Continue");
		HTTP_RETURN_CODE.put(101, "Switching Protocols");
		HTTP_RETURN_CODE.put(102, "Processing");

		HTTP_RETURN_CODE.put(200, "OK");
		HTTP_RETURN_CODE.put(201, "Created");
		HTTP_RETURN_CODE.put(202, "Accepted");
		HTTP_RETURN_CODE.put(203, "Non-Authoritative Information");
		HTTP_RETURN_CODE.put(204, "No Content");
		HTTP_RETURN_CODE.put(205, "Reset Content");
		HTTP_RETURN_CODE.put(206, "Partial Content");
		HTTP_RETURN_CODE.put(207, "Multi-Status");

		HTTP_RETURN_CODE.put(300, "Multiple Choices");
		HTTP_RETURN_CODE.put(301, "Moved Permanently");
		HTTP_RETURN_CODE.put(302, "Move temporarily");
		HTTP_RETURN_CODE.put(303, "See Other");
		HTTP_RETURN_CODE.put(304, "Not Modified");
		HTTP_RETURN_CODE.put(305, "Use Proxy");
		HTTP_RETURN_CODE.put(306, "Switch Proxy");
		HTTP_RETURN_CODE.put(307, "Temporary Redirect");

		HTTP_RETURN_CODE.put(400, "Bad Request");
		HTTP_RETURN_CODE.put(401, "Unauthorized");
		HTTP_RETURN_CODE.put(402, "Payment Required");
		HTTP_RETURN_CODE.put(403, "Forbidden");
		HTTP_RETURN_CODE.put(404, "Not Found");
		HTTP_RETURN_CODE.put(405, "Method Not Allowed");
		HTTP_RETURN_CODE.put(406, "Not Acceptable");
		HTTP_RETURN_CODE.put(407, "Proxy Authentication Required");
		HTTP_RETURN_CODE.put(408, "Request Timeout");
		HTTP_RETURN_CODE.put(409, "Conflict");
		HTTP_RETURN_CODE.put(410, "Gone");
		HTTP_RETURN_CODE.put(411, "Length Required");
		HTTP_RETURN_CODE.put(412, "Precondition Failed");
		HTTP_RETURN_CODE.put(413, "Request Entity Too Large");
		HTTP_RETURN_CODE.put(414, "Request-URI Too Long");
		HTTP_RETURN_CODE.put(415, "Unsupported Media Type");
		HTTP_RETURN_CODE.put(416, "Requested Range Not Satisfiable");
		HTTP_RETURN_CODE.put(417, "Expectation Failed");
		HTTP_RETURN_CODE.put(421, "There are too many connections from your internet address");
		HTTP_RETURN_CODE.put(422, "Unprocessable Entity");
		HTTP_RETURN_CODE.put(423, "Locked");
		HTTP_RETURN_CODE.put(424, "Failed Dependency");
		HTTP_RETURN_CODE.put(425, "Unordered Collection");
		HTTP_RETURN_CODE.put(426, "Upgrade Required");
		HTTP_RETURN_CODE.put(449, "Retry With");

		HTTP_RETURN_CODE.put(500, "Internal Server Error");
		HTTP_RETURN_CODE.put(501, "Not Implemented");
		HTTP_RETURN_CODE.put(502, "Bad Gateway");
		HTTP_RETURN_CODE.put(503, "Service Unavailable");
		HTTP_RETURN_CODE.put(504, "Gateway Timeout");
		HTTP_RETURN_CODE.put(505, "HTTP Version Not Supported");
		HTTP_RETURN_CODE.put(506, "Variant Also Negotiates");
		HTTP_RETURN_CODE.put(507, "Insufficient Storage");
		HTTP_RETURN_CODE.put(509, "Bandwidth Limit Exceeded");
		HTTP_RETURN_CODE.put(510, "Not Extended");

		HTTP_RETURN_CODE.put(600, "Unparseable Response Headers");
	}

	public static String getBcUrl(String url, Map<String, String> params) {
		StringBuffer sb = new StringBuffer();
		if ((params != null) && (params.size() > 0)) {
			for (Entry<String, String> e : params.entrySet()) {
				sb.append(e.getKey());
				sb.append("=");
				sb.append(e.getValue());
				sb.append("&");
			}
			sb.substring(0, sb.length() - 1);
		}
		sb.trimToSize();
		return url + "?" +  sb.toString();
	}

	public static String http(String url, Map<String, String> params) throws Exception {
		URL u = null;
		HttpURLConnection con = null;
		// 构建请求参数
		StringBuffer sb = new StringBuffer();
		if ((params != null) && (params.size() > 0)) {
			for (Entry<String, String> e : params.entrySet()) {
				sb.append(e.getKey());
				sb.append("=");
				sb.append(e.getValue());
				sb.append("&");
			}
			sb.substring(0, sb.length() - 1);
		}
		sb.trimToSize();
		// 尝试发送请求
		try {
			u = new URL(url);
			con = (HttpURLConnection) u.openConnection();
			con.setRequestMethod("POST");
			con.setDoOutput(true);
			con.setDoInput(true);
			con.setUseCaches(false);
			con.setRequestProperty("Content-Type","application/x-www-form-urlencoded; text/html; charset=utf-8");
			con.setRequestProperty("isrsa", isrsa);
			con.setConnectTimeout(30000);
			con.setReadTimeout(30000);
			OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream(), "GBK");
			osw.write(sb.toString());
			osw.flush();
			osw.close();
		}  finally {
			if (con != null) {
				con.disconnect();
			}
		}
		// 读取返回内容
		StringBuffer buffer = new StringBuffer();
		BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
		String temp;
		while ((temp = br.readLine()) != null) {
			buffer.append(temp);
			buffer.append("\n");
		}
		buffer.trimToSize();
		return buffer.toString();
	}

	private static byte[] inputStream2Byte(InputStream input) throws Exception{
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		int len = 0;
		byte[] data = new byte[1024];
		while ((len = input.read(data, 0, data.length)) != -1) {
			baos.write(data, 0, len);
		}
		baos.flush();
		byte[] buffer = baos.toByteArray();
		return buffer;
    }

	private static String urlTransCodeing(String url){
		return url.replace("\r\n", "").replace("\"", "%22").replace("{", "%7b").replace("}", "%7d").replace("|", "%124"); //.replace("&", "%26")
	}

	private static SSLConnectionSocketFactory getSSLConnectionSocketFactory() throws Exception {
		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}
			@Override
			public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
			}
			@Override
			public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
			}
		}};
		SSLContext sc = SSLContext.getInstance("TLSv1");
//		SSLContext sc = SSLContexts.custom().useTLS().build();
		sc.init(null, trustAllCerts, new java.security.SecureRandom());
		// return new SSLConnectionSocketFactory(sc, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
		return new SSLConnectionSocketFactory(sc, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
	}

	/*private static ConnectionKeepAliveStrategy keepAliveStrategy = new DefaultConnectionKeepAliveStrategy() {
	    @Override
	    public long getKeepAliveDuration(
	        HttpResponse response, HttpContext context) {
	            long keepAlive = super.getKeepAliveDuration(response, context);
	            if (keepAlive == -1) {
	            	// 如果服务器没有设置keep-alive这个参数，我们就把它设置成5秒
	            	System.out.println("keepAlive==-1" );
	                keepAlive = 2000;
	            }
	            return keepAlive;
	    }
	};*/

	public static CloseableHttpClient getSslHttpClient() throws Exception {
        RequestConfig config = RequestConfig.custom().setConnectTimeout(20000).setSocketTimeout(30000).build();
        //CloseableHttpClient httpclient  = HttpClients.custom().setSSLSocketFactory(getSSLConnectionSocketFactory()).setDefaultRequestConfig(config).setKeepAliveStrategy(keepAliveStrategy).build();
        CloseableHttpClient httpclient  = HttpClients.custom().setSSLSocketFactory(getSSLConnectionSocketFactory()).setDefaultRequestConfig(config).build();
        return httpclient;
	}

//	/*
//	 * 通过PoolingHttpClientConnectionManager连接池来获取httpClient链接
//	 * 重要：现在暂时不要用，调用https连接时，会产生socketException,安全认证问题，还未解决
//	 */
//	public static CloseableHttpClient getCmHttpClient() throws Exception{
//
//		if (cm == null) {
//			//连接池没创建的时候，先创建连接池
//			ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
//			Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
//					.register("http", plainsf)
//					.register("https", getSSLConnectionSocketFactory())
//					.build();
//			cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
//			cm.setMaxTotal(200); // 连接池最大并发连接数 将最大连接数增加到200
//			cm.setDefaultMaxPerRoute(100); // 单路由最大并发数 将每个路由基础的连接增加到100
//		}
//
//		//请求重试处理
//		HttpRequestRetryHandler httpRequestRetryHandler = getHttpRequestRetryHandler();
//		//配置连接和socket过期时间
//		RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(30000).setConnectTimeout(30000).setSocketTimeout(600000).build();
//		//通过连接池获取连接
//		return HttpClients.custom().setConnectionManager(cm).setRetryHandler(httpRequestRetryHandler).setDefaultRequestConfig(config).build();
//	}
//
//	//请求重试处理
//	private static HttpRequestRetryHandler getHttpRequestRetryHandler(){
//		//请求重试处理
//		return new HttpRequestRetryHandler() {
//			public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
//				if (executionCount >= 5) {// 如果已经重试了5次，就放弃
//					return false;
//				}
//				if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接，那么就重试
//					return true;
//				}
//				if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
//					return false;
//				}
//				if (exception instanceof InterruptedIOException) {// 超时
//					return false;
//				}
//				if (exception instanceof UnknownHostException) {// 目标服务器不可达
//					return false;
//				}
//				if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
//					return false;
//				}
//				if (exception instanceof SSLException) {// ssl握手异常
//					return false;
//				}
//
//				HttpClientContext clientContext = HttpClientContext.adapt(context);
//				HttpRequest request = clientContext.getRequest();
//				// 如果请求是幂等的，就再次尝试
//				if (!(request instanceof HttpEntityEnclosingRequest)) {
//					return true;
//				}
//				return false;
//			}
//		};
//	}

	public static String doGet(String url, Map<String, String> params) throws Exception {
		if (StringUtils.isBlank(url)) {
			throw new Exception("URL请求地址为空!");
		}
		long start = System.nanoTime();
		if (params != null && !params.isEmpty()) {
			List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());
			for (Map.Entry<String, String> entry : params.entrySet()) {
				String value = entry.getValue();
				if (value != null) {
					pairs.add(new BasicNameValuePair(entry.getKey(), value));
				}
			}
			url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs,"UTF-8"));
		}
		url = urlTransCodeing(url);
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("isrsa", isrsa);
        CloseableHttpClient httpClient = getSslHttpClient();
        CloseableHttpResponse response = httpClient.execute(httpGet);
		try {
	        int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_OK != statusCode) {
            	httpGet.releaseConnection();
            	log.error("Get失败请求耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒, url:" + url + ",请求数据:" +params +",状态码:"+statusCode+",描述："+HTTP_RETURN_CODE.get(statusCode));
                throw new Exception("HttpUtils->Get," + "url:" + url +",error status code:" + statusCode);
            }
	        HttpEntity entity = response.getEntity();
	        String result  = EntityUtils.toString(entity, "UTF-8");
            // EntityUtils.consume(entity); //关闭HttpEntity的流
            EntityUtils.consumeQuietly(entity); //关闭HttpEntity的流不抛异常
            log.info("Get耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒, url:" + url + ",result:" + result);
            return result;
		} finally {
			response.close();
			httpClient.close();
		}
	}


	public static String doGet(String url) throws Exception {
		return doGet(url, null);
	}


	/**
	 *
	 * getInputStream:(读取URL对应数据流). <br/>
	 * Date:   2015年5月22日 下午2:55:22 <br/>
	 * @author 李鑫
	 * @param url
	 * @return
	 * @throws Exception
	 */
	private static InputStream getInputStream(String url) throws Exception {
		if (StringUtils.isBlank(url)) {
			throw new Exception("URL请求地址为空!");
		}
		url = urlTransCodeing(url);
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("isrsa", isrsa);
        CloseableHttpClient httpClient = getSslHttpClient();
        CloseableHttpResponse response = httpClient.execute(httpGet);
		try {
	        int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_OK != statusCode) {
            	httpGet.releaseConnection();
            	log.error("读取URL对应数据流发生错误：url:" + url + ",状态码:"+statusCode+",描述："+HTTP_RETURN_CODE.get(statusCode));
                throw new Exception("HttpUtils->Get," + "url:" + url +",error status code:" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            // 判断返回类型
            String contentType = response.getFirstHeader("Content-Type").getValue();
            if ("text/plain".equals(contentType)){
    	        //{"errcode":40001,"errmsg":"invalid credential, access_token is invalid or not latest"}
            	String result = EntityUtils.toString(entity, "UTF-8");
            	JSONObject resultJson = JSONObject.parseObject(result);
	        	if (StringUtils.isNotBlank(resultJson.getString("errcode"))) {
	 	        	throw new Exception(resultJson.getString("errmsg"));
	 	        }
	        	return null;
            }
            byte[] bytes = inputStream2Byte(entity.getContent());
	        InputStream instream = new ByteArrayInputStream(bytes);
            // EntityUtils.consume(entity);
            EntityUtils.consumeQuietly(entity);
            return instream;
		} finally {
			response.close();
			httpClient.close();
		}
	}

	/**
	 *
	 * doPost:(带参数POST). <br/>
	 * Date:   2015年5月5日 上午10:44:20 <br/>
	 * @author 李鑫
	 * @param url
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static String doPost(String url, Map<String, String> params) throws Exception {
		if (StringUtils.isBlank(url)) {
			throw new Exception("URL请求地址为空!");
		}
		long start = System.nanoTime();
		url = urlTransCodeing(url);
		HttpPost httpPost = new HttpPost(url);
		httpPost.setHeader("isrsa", isrsa);
		if (params != null && !params.isEmpty()) {
			List<NameValuePair> formParams = new ArrayList<NameValuePair>(); // 构建POST请求的表单参数
			for (Map.Entry<String, String> entry : params.entrySet()) {
				formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
			}
			if(formParams != null && formParams.size() > 0){
				httpPost.setEntity(new UrlEncodedFormEntity(formParams, "UTF-8"));
			}
		}
		CloseableHttpClient httpClient = getSslHttpClient();
		CloseableHttpResponse response = httpClient.execute(httpPost);
		try {
			int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_OK != statusCode) {
            	httpPost.releaseConnection();
            	log.error("Post失败请求耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒, url:" + url + ",请求数据:" + params+",状态码:"+statusCode+",描述："+HTTP_RETURN_CODE.get(statusCode));
                throw new Exception("HttpUtils->Post," + "url:" + url + ",error status code:" + statusCode);
            }
			HttpEntity entity = response.getEntity();
			String result = EntityUtils.toString(entity, "UTF-8");
            // EntityUtils.consume(entity);
            EntityUtils.consumeQuietly(entity);
            log.info("Post耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒, url:" + url + ",params:" + params.toString() + ",result:" + result);
            return result;
		} finally {
			response.close();
			httpClient.close();
		}
	}

	/**
	 *
	 * doPost:(Post请求数据流). <br/>
	 * Date:   2015年5月5日 上午10:44:37 <br/>
	 * @author 李鑫
	 * @param url
	 * @param postData
	 * @param contentType
	 * @return
	 * @throws Exception
	 */
	public static String doPost(String url, String postData, String contentType) throws Exception {
		if (StringUtils.isBlank(url)) {
			throw new Exception("URL请求地址为空!");
		}
		long start = System.nanoTime();
		url = urlTransCodeing(url);
		HttpPost httpPost = new HttpPost(url);
		httpPost.setHeader("isrsa", isrsa);
		StringEntity postDataEntity = new StringEntity(postData,"utf-8");//解决中文乱码问题
		postDataEntity.setContentEncoding("UTF-8");
		postDataEntity.setContentType(contentType);
		httpPost.setEntity(postDataEntity);
		CloseableHttpClient httpClient = getSslHttpClient();
		CloseableHttpResponse response = httpClient.execute(httpPost);
		try {
			int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_OK != statusCode) {
            	httpPost.releaseConnection();
            	log.error("Post失败请求耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒, url:" + url + ",请求数据:" + postData+",状态码:"+statusCode+",描述："+HTTP_RETURN_CODE.get(statusCode));
                throw new Exception("HttpUtils->doPost," + "url:" + url + ",error status code:" + statusCode);
            }
			HttpEntity entity = response.getEntity();
			String result = EntityUtils.toString(entity, "UTF-8");
            // EntityUtils.consume(entity);
			EntityUtils.consumeQuietly(entity);
            log.info("Post耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒, url:" + url + ",postData:" + postData + ",result:" + result);
            return result;
		} finally {
			response.close();
			httpClient.close();
		}
	}

	/*
	 * 请求JSON数据
	 * */
	public static String doPostJson(String url, String postData) throws Exception {
		return doPost(url, postData, "application/json");
	}

	/* 请求XML数据*/
	public static String doPostXml(String url, String postData) throws Exception {
		return doPost(url, postData, "application/xml");
	}


	/**
	 *
	 * doGetObject:(Get方式返回Object-JavaBean对象). <br/>
	 * Date:   2015年5月5日 上午10:42:12 <br/>
	 * @author 李鑫
	 * @param url
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	public static <T> T doGetObject(String url, Class<T> clazz) throws Exception{
		//return JSON.toJavaObject(doGetJSONObject(url), clazz);
		return JSON.parseObject(doGet(url), clazz);
	}

	/**
	 *
	 * doGetJSONObject:(Get方式返回JSONObject). <br/>
	 * Date:   2015年5月5日 上午10:41:24 <br/>
	 * @author 李鑫
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static JSONObject doGetJSONObject(String url) throws Exception{
		return JSON.parseObject(doGet(url));
	}

	/**
	 *
	 * doGetJSONArray:(Get方式返回JsonArray). <br/>
	 * Date:   2015年5月5日 上午10:42:44 <br/>
	 * @author 李鑫
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static JSONArray doGetJSONArray(String url) throws Exception{
		return JSON.parseArray(doGet(url));
	}


	/**
	 *
	 * doGetArray:(Get方式返回List对象). <br/>
	 * Date:   2015年5月5日 上午10:43:38 <br/>
	 * @author 李鑫
	 * @param url
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	public static <T> List<T> doGetList(String url, Class<T> clazz) throws Exception{
		return JSON.parseArray(doGet(url), clazz);
	}

	/**
	 *
	 * doPostJSONObject:(Post方式返回JSONObject). <br/>
	 * Date:   2015年5月5日 上午10:41:44 <br/>
	 * @author 李鑫
	 * @param url
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static JSONObject doPostJSONObject(String url, Map<String, String> params) throws Exception{
		return JSON.parseObject(doPost(url, params));
	}

	/**
	 *
	 * doPostJSONObject:(这里用一句话描述这个方法的作用). <br/>
	 * Date:   2015年7月16日 上午11:40:33 <br/>
	 * @author 李鑫
	 * @param url
	 * @param postData
	 * @return
	 * @throws Exception
	 */
	public static JSONObject doPostJSONObject(String url, String postData) throws Exception{
		return JSON.parseObject(doPostJson(url, postData));
	}

	/**
	 *
	 * doPostObject:(这里用一句话描述这个方法的作用). <br/>
	 * Date:   2015年9月8日 下午1:55:29 <br/>
	 * @author 李鑫
	 * @param url
	 * @param postData
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	public static <T> T doPostObject(String url, String postData, Class<T> clazz) throws Exception{
		return JSON.parseObject(doPostJson(url, postData), clazz);
	}


	/**
	 *
	 * doGetImageBase64Url:(Base64,URL数据). <br/>
	 * Date:   2015年5月22日 下午3:04:55 <br/>
	 * @author 李鑫
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static String doGetUrlToBase64(String url) throws Exception{
		InputStream inStream = getInputStream(url);
		try {
			byte[] bytes = inputStream2Byte(inStream);
			return Base64.encodeBase64String(bytes); // 返回Base64编码过的字节数组字符串
		} finally {
			if (inStream != null)
				inStream.close();
		}
	}

	/**
	 *
	 * doGetImageFileUrl:(存为文件,URL数据). <br/>
	 * Date:   2015年5月22日 下午3:14:47 <br/>
	 * @author 李鑫
	 * @param url
	 * @param fileName
	 * @throws Exception
	 */
	public static void doGetUrlToImageFile(String url, String fileName) throws Exception{
		InputStream inStream = getInputStream(url);
		FileOutputStream outputStream = new FileOutputStream(fileName);
		FileCopyUtils.copy(inStream, outputStream);
	}

	/**
	 *
	 * doPostCrm7JSONObject(Crm7专属POST接口请求)
	 * @Description: TODO
	 * @author Mobile Web Group-lx
	 * @date 2017年1月12日 上午10:57:57
	 *
	 * @param url
	 * @param postData
	 * @return
	 * @throws Exception
	 * @return JSONObject
	 */
	public static JSONObject doPostCrm7JSONObject(String url, JSONObject postParam) throws Exception{
		long start = System.nanoTime();

		// postParam.put("callerId", postParam.containsKey("callerId")?postParam.getString("callerId"):"wxapi");
		//加密前参数
		String orginParam = postParam.toJSONString();

		if (postParam.containsKey("data")){
			try {
				JSONObject encryptParam = postParam.getJSONObject("data");
				String encryptdData = RSAUtils.encryptByPublicKey(encryptParam.toString(), Consts.CRM7_PUBLIC_KEY);
				postParam.put("data", encryptdData);
			} catch (Exception e) {
				log.error("Crm7公钥加密参数数据失败" + e.toString() + ",url:" + url + ",postParam:" + postParam);
				throw e;
			}
		}
		JSONObject reJsonObject = HttpUtil.doPostJSONObject(url, postParam.toJSONString());
		// 接口返回异常
		if (!StringUtils.equals("200", reJsonObject.getString("code"))){
			if ("会员卡信息未找到".equals(reJsonObject.getString("msg"))) {
				//非会员正常，不输出日志
				return reJsonObject;
			}
			log.error("调用CRM7接口url:" + url + ",耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒,加密前参数：" + orginParam + ",reJsonObject:" + reJsonObject);
			return reJsonObject;
		}
		// 接口返回正常，进行数据解析
		if (reJsonObject.containsKey("content")){
			try {
				String decryptContent = RSAUtils.decryptByPublicKey(reJsonObject.getString("content"), Consts.CRM7_PUBLIC_KEY);
				reJsonObject.put("content", decryptContent);
				log.info("调用CRM7接口url:" + url + ",含解密,耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒,加密前参数：" + orginParam + ",解密后返回值:" + decryptContent);
				return reJsonObject;
			} catch (Exception e) {
				log.error("Crm7公钥解密参数数据失败" + e.toString() + ",url:" + url + ",加密前参数:" + orginParam + ",reJsonObject:" + reJsonObject);
				throw e;
			}
		}
		log.info("调用CRM7接口url" + url + ",无解密,耗时" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)) + "毫秒,加密前参数：" + orginParam);
		return reJsonObject;
	}



	/**
	 * downloadFilesByUrls(根据url生成文件，并保存到服务器)
	 * @author Mobile Web Group-lff
	 * @date 2016年6月12日 上午8:37:16
	 *
	 * @param urlToImageMap：key：图片url，value：保存后的文件的全路径文件名
	 */
	public static void downloadFilesByUrls(Map<String, String> urlToImageMap) {

		if (urlToImageMap == null || urlToImageMap.isEmpty()) {
			//空map的情况下，直接返回
			return;
		}

		CloseableHttpClient httpClient = null;
		try {
			httpClient = getSslHttpClient();

			//得到url集合，用于循环
			Set<String> urls = urlToImageMap.keySet();
			int urlCounts = urls.size();
			//避免创建线程数量过多，给服务器造成压力，设置最大线程数为10
			int threadCounts = urlCounts <= HTTP_CLIENT_MAX_THREAD_COUNTS ? urlCounts : HTTP_CLIENT_MAX_THREAD_COUNTS;
			//线程池和线程计数器，线程计数器的大小，必须设置为集合的大小
			ExecutorService executors = Executors.newFixedThreadPool(threadCounts);

			for (String url : urls) {
				//启动多线程
				executors.execute(new DownloadRunnable(httpClient, url, urlToImageMap.get(url)));
			}

			//关闭多线程池
			executors.shutdown();
			//子线程全部执行完之前，最长等待1小时
			executors.awaitTermination(1, TimeUnit.HOURS);
		} catch (Exception e) {
			//记录log
			log.error("根据url保存文件失败：" + e.toString());
		} finally {
			if (httpClient != null ) {
				try {
					httpClient.close();
				} catch (IOException e) {
					log.error("httpClient关闭失败：" + e.toString());
				}
			}
		}

	}

	/**
	 * @ClassName: HttpRequestRunnable
	 * @Description: http请求后的多线程执行类
	 * @author Mobile Web Group-lff
	 * @date 2016年6月15日 下午1:27:39
	 *
	 */
	private static class DownloadRunnable implements Runnable {

		private CloseableHttpClient httpClient;
		private String url;//要下载的url
		private String fileFullName;//要保存的文件全路径

		/**
		 * DownloadRunnable(构造函数)
		 * @author Mobile Web Group-lff
		 * @date 2016年6月15日 上午9:43:40
		 *
		 * @param httpClient
		 * @param url
		 * @param fileFullName:要保存的文件全路径
		 * @param countDownLatch
		 */
		public DownloadRunnable (CloseableHttpClient httpClient, String url, String fileFullName) {
			this.httpClient = httpClient;
			this.url = url;
			this.fileFullName = fileFullName;
		}

		@Override
		public void run() {
			CloseableHttpResponse response = null;
			try {
				//http请求
				HttpGet httpGet = new HttpGet(url);
				//下载大量文件，比如二维码时，需要的socket时间比较长，timeout时间延长到10分钟
				RequestConfig config = RequestConfig.custom().setConnectTimeout(20000).setSocketTimeout(600000).build();
				httpGet.setConfig(config);
				httpGet.setHeader("isrsa", HttpUtil.isrsa);

				response = this.httpClient.execute(httpGet, HttpClientContext.create());

				int statusCode = response.getStatusLine().getStatusCode();
				if (HttpStatus.SC_OK != statusCode) {
					//url请求失败，释放连接
					httpGet.releaseConnection();
					log.error("http请求执行失败,状态码:"+statusCode+",描述：" + HttpUtil.HTTP_RETURN_CODE.get(statusCode));
				}else{
					//url请求成功，根据业务处理response,BufferedInputStream一定要加，不然读取太慢，易出SSLException
					FileCopyUtils.copy(new BufferedInputStream(response.getEntity().getContent()), new FileOutputStream(new File(fileFullName)));//spring框架提供的文件copy功能，利用一个固定的4M缓存进行copy,完成后自动关闭输入和输出流
				}
			} catch (Exception e) {
				log.error("根据url保存文件失败：" + fileFullName + ":" + e.toString());
			} finally {
				try {
					if (response != null) {
						response.close();
					}
				} catch (IOException e) {
					log.error("关闭response失败：" + e.toString());
				}
			}
		}
	}



























	public static void main(String[] args) {

		//测试连接的时间
		CloseableHttpClient httpClient = null;
		try {
			httpClient = HttpUtil.getSslHttpClient();
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		// URL列表
		List<String> urisToGet = new ArrayList<String>();
		for (int i = 0; i < 2000; i++) {
			urisToGet.add("https://baidu.com");
		}

		long start = System.nanoTime();
		try {
			int pagecount = urisToGet.size();

			//避免创建线程数量过多，给服务器造成压力，设置最大线程数为100
			int max_thread_counts = pagecount <= 100 ? pagecount : 100;
			ExecutorService executors = Executors.newFixedThreadPool(max_thread_counts);

            CountDownLatch countDownLatch = new CountDownLatch(pagecount);
            for(int i = 0; i< pagecount;i++){
                HttpGet httpget = new HttpGet(urisToGet.get(i));
                //启动线程抓取
                executors.execute(new GetRunnable(httpClient,httpget,countDownLatch));
            }

			countDownLatch.await();
			executors.shutdown();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			try {
				if (httpClient != null) httpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			System.out.println("线程" + Thread.currentThread().getName() + "," + System.currentTimeMillis() + ", 所有线程已完成，开始进入下一步！");
		}

		System.out.println("consume -> " + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start))
);


//		String url = "http://192.168.4.27:888/szslservice/WeiXinInterfaceAction.a?WeiXinCardCusInfo&weixinID=123&cardNo=1001000000000006&cusCount=50";
//		String url = "http://192.168.4.27:666/lbzs/api/user/login.action?param={%22password%22:%22456%22,%22userName%22:%22123%22,%22valCode%22:%22389%22}";
		//String url = "http://192.168.4.187:8080/i5xwxplus/test/testjson.html";
		//System.out.println(doGet(url));

//		String url = "http://192.168.4.187:8080/i5xwxplus/test/testjson.html";
		//String url = "http://192.168.4.27:666/api/tcsl/DownloadItemClassList.htm?data={\"mcID\":%1$s,\"rowsPerPage\":9999,\"pageNo\":1}";
		//String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ONWWEY9o_3DDFQUCHO4fm4ElpLOHYs2Ome2jY9m2dPRyxlA1aGRboRA6YgfIKlGozQxhOb058JPOdmxOzoxeGpipu6kQRU5PeQim5m1_T2k&openid=%1$s&lang=zh_CN";
		//url = String.format(url, "onWmpjs1hzuXiMr1ZaRNmZvJ0Pr0");
		//try {
			//System.out.println(doGet(url));

			//JSONObject user = doGetJSONObject(url);
			//System.out.println(user.getString("nickname"));
			//System.out.println(user.getString("sex"));
			//System.out.println(user.getString("headimgurl"));

			//String wsUrl = "http://cs.wuuxiang.com:888/szslservice/WeiXinInterfaceAction.a?WeiXinGetShopInfo&weixinPlatID=gh_81f02eaef2fe";
	       // JSONArray jsonArray = HttpUtil.doGetJSONArray(wsUrl);
	        //JSONObject jsonObj = jsonArray.getJSONObject(0);

	       // System.out.println(jsonObj.getString("shopCode"));

//			System.out.println(doPostJson(url, ""));
/*			String url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID";
			url = url.replace("ACCESS_TOKEN", "A8rb_s-h3oOgdG62b78r6H3dpLl9lSCcfySrhyLDpVvqsfXIRo6rJLc2--WnWHHq4L278Afy6gvZ8ev2rHBFtT9xygTPPn0MQxCrutxNlv0");
			url = url.replace("MEDIA_ID", "74lXJPSPusnJjklnf2EzmEo4L2rJZjH1KvuTD4AdY6kEd1buXvj4D6w6o9231lc-");
			System.out.println(doGetUrlToBase64(url));*/

			//String url = "http://cs.wuuxiang.com:888/szslservice/WeiXinInterfaceAction.a?CommGetCard&open_ID=-dkz35HA8OLCx7UqlqkWOXJo810GYn4ZMWFop0Shb9+Z1MmxvWOQCcSGdzmtKHuP01&pLat_ID=2015030900034621&fromType=1&mobile=&sex=12210&email=&name=%C4%BD%D7%CF&birthday=";


			//http://cs.wuuxiang.com:888/szslservice/WeiXinInterfaceAction.a?CommGetCard&open_ID=-dkz35HA8OLCx7UqlqkWOXJo810GYn4ZMWFop0Shb9+Z1MmxvWOQCcSGdzmtKHuP01&pLat_ID=2015030900034621&fromType=1&mobile=&sex=12210&email=&name=%C4%BD%D7%CF&birthday=


			/*String url = "http://cs.wuuxiang.com:888/szslservice/WeiXinInterfaceAction.a?" + "CommGetCard&open_ID=OPENID&pLat_ID=PLATID&fromType=0&mobile=MOBILE&sex=SEX&email=EMAIL&name=USERNAME&birthday=BIRTHDAY";
			url = url.replace("PLATID", "gh_86d9c77ec638").replace("OPENID", "oR6_JjiMA_Z1OPZBqAgCAhifNxpc").replace("MOBILE", "13821381520");
			url = url.replace("SEX", "12210").replace("BIRTHDAY", "1984-05-18").replace("EMAIL", "ankebeier@qq.com").replace("USERNAME", ToolUtil.encodeGBK("幕紫"));
			JSONObject jsonCardObj = HttpUtil.doGetJSONObject(url);
			System.out.println(jsonCardObj.toJSONString());
			CrmCards crmCards = HttpUtil.doGetObject(url, CrmCards.class);
			//System.out.println(crmCards.toString());
			System.out.println(JSONObject.toJSONString(crmCards));*/


			//String textMsg = "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"CONTENT\"}}";
			//textMsg = textMsg.replace("OPENID", "oR6_JjoEn5HnsZu29RAA5bEk6enA").replace("CONTENT", "正在为您转入多客服系统,请稍后!");
			//String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=abN1qjn8rAb63mTk1xOWfLaXFjdCOo3wEzz_yl-klyGT73aYrDX_WQuSRLbgGft6cPCBDxVUiUV2Pfgg-tyH0Fg8_Gy1biZjK_PZ2gQeXOcQ2yaHJABQMJQMWesjhfwk";
			//JSONObject resultJson = HttpUtil.doPostJSONObject(url, textMsg);
			//System.out.println(resultJson.toString());

			//String ssss = "{\"component_appid\":\"wx9c77f4174cce50be\",\"authorizer_appid\":\"wx57175023a9285f89\"}";
			//System.out.println(HttpUtil.doPostJson("https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=is6ylCnxc3Bewf7dX5cEz-4bAb9RG15UuXOsCt-biY6CglaaftcECoeXCcelEZ2d7iPdJh3fpqS9g63FZ9K9J5iuSXJPXdXx4ZzHdOtoHl0SNRbADAWRV", ssss));;





		/*	oR6_JjoEn5HnsZu29RAA5bEk6enA
			gh_86d9c77ec638*/

			/*String url = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=jWdcyp03HMB-8l8lr6PJS5ftlcJ-wrl-asfz61NcLGvNmTEjKYL_CsJ9-zYWI_wXq12z98GpxWj9RbuFiM2v__Yn49mSkr_LU7gwqsC71M0";
				System.out.println(doPostJson(url, "{\"component_appid\":\"wx9c77f4174cce50be\",\"authorization_code\":\"queryauthcode@@@QjC87GXtlH_S7wqx2uu2qKrPvoXcYH1nU1VR6S2CKeQ8FPiNXUfuZPKvrBrbNlsC\"}"));
			*/

		//} catch (Exception e) {
		//	e.printStackTrace();
		//}
		/*
		try {
			throw new MyException("123");
		} catch (Exception e) {
			System.out.println("dd" + e.getClass().getName().toString());
			System.out.println("cc" + e.fillInStackTrace().toString());
			System.out.println(e.getClass().getSimpleName());

		}*/

	}

    private static class GetRunnable implements Runnable {
        private CountDownLatch countDownLatch;
        private final CloseableHttpClient httpClient;
        private final HttpGet httpget;

        public GetRunnable(CloseableHttpClient httpClient, HttpGet httpget, CountDownLatch countDownLatch){
            this.httpClient = httpClient;
            this.httpget = httpget;

            this.countDownLatch = countDownLatch;
        }
        @Override
        public void run() {
            CloseableHttpResponse response = null;
            try {
                response = httpClient.execute(httpget,HttpClientContext.create());
                HttpEntity entity = response.getEntity();
                // EntityUtils.consume(entity);
                EntityUtils.consumeQuietly(entity);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();

                try {
                    if(response != null)
                        response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
